package cn.mrlong.base.utils.websocket

import android.os.Handler
import android.os.Message
import android.util.Log
import okhttp3.*
import okio.ByteString

/**
 * 常用okhttp 的websocket实现与服务器交互数据
 */
class WSManger private constructor() : WebSocketListener() {
    private var wsUrl: String? = null//连接服务器地址
    private var webSocket: WebSocket? = null
    private var status: ConnectStatus? = null
    private val HANDLER_MESSAGE_RECONN = 0x00001
    private var handler: Handler? = object : Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            if (HANDLER_MESSAGE_RECONN == msg.what) {
                reConn()
            }
        }
    }

    private fun reConn() {
        if (status != ConnectStatus.OPEN || status != ConnectStatus.CONNECTING) {
            client.dispatcher.cancelAll()
            val request = Request
                .Builder()
                .removeHeader("User-Agent")
                .addHeader("User-Agent", userAgent!!)
                .url(wsUrl!!)
                .build()
            webSocket = client.newWebSocket(request, this)
            //changeStatus(ConnectStatus.CONNECTING)
            log("重连：" + Tools.format(System.currentTimeMillis()))
            handler?.removeMessages(HANDLER_MESSAGE_RECONN)
        }
    }

    private val client: OkHttpClient by lazy {
        OkHttpClient.Builder().retryOnConnectionFailure(true).build()
    }
    private var mSocketIOCallback: WebSocketCallBack? = null

    /**
     * 第三步 监听回调
     */
    interface WebSocketCallBack {
        fun onOpen()
        fun onClose()
        fun onError(t: Throwable)
        fun onMessage(text: String)
        fun onConnect(status: ConnectStatus?)
    }

    public fun setSocketCallBack(callBack: WebSocketCallBack): WSManger {
        this.mSocketIOCallback = callBack
        return this
    }

    /**
     * 第一步先设置url
     */
    public fun wsUrl(url: String): WSManger {
        this.wsUrl = url
        return this
    }

    //单例模式
    companion object {
        val TAG: String = "WebSocket = >";
        val instance: WSManger by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            WSManger()
        }
    }

    /**
     * 第二部 连接初始化
     * 1、wsUrl地址不能为null
     * 2、本例UserAgent不能为null，后台服务器要拦截 （err:response is 418 ）
     */
    @Synchronized
    fun connect() {
        if (null == wsUrl) {
            mSocketIOCallback?.onError(Exception("WebSockt 初始化时候 连接地址不能为null"))
            return
        }
        if (null == userAgent) {
            mSocketIOCallback?.onError(Exception("UseAgent is null"))
            return
        }
        try {
            client.dispatcher.cancelAll()
            val request = Request
                .Builder()
                .removeHeader("User-Agent")
                .addHeader("User-Agent", userAgent!!)
                .url(wsUrl!!)
                .build()
            webSocket = client.newWebSocket(request, this)
            changeStatus(ConnectStatus.CONNECTING)
            checkHeart()
            log("init 地址:${wsUrl}")
        } catch (e: Exception) {
            e.printStackTrace()
            mSocketIOCallback?.onError(e)
        }
    }


    private val reconnectTimeout = 10 * 1000

    /**
     * 重连机制 默认10s 重连
     * 开启同步机制 防止多个重连
     */
    @Synchronized
    public fun reConnect() {
        if (this.status != ConnectStatus.RECONNECTING && this.status != ConnectStatus.CLOSED) {
            changeStatus(ConnectStatus.RECONNECTING)
            handler?.sendEmptyMessageDelayed(HANDLER_MESSAGE_RECONN, reconnectTimeout.toLong())
        }
    }

    /**
     * websocket打开 可以执行下一步操作
     * 比如ping 绑定用户等
     */
    override fun onOpen(webSocket: WebSocket, response: Response) {
        super.onOpen(webSocket, response)
        log("open")
        changeStatus(ConnectStatus.OPEN)
        mSocketIOCallback?.onOpen()
    }

    /**
     * 关闭
     **/
    override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
        super.onClosed(webSocket, code, reason)
        changeStatus(ConnectStatus.CLOSED)
        log("onClosed")
    }

    /**
     * 关闭中
     * 默认关闭时 将重连
     */
    override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
        super.onClosing(webSocket, code, reason)
        mSocketIOCallback?.onClose()
        changeStatus(ConnectStatus.CLOSING)
        log("onClosing")
    }

    /**
     * error处理
     * 当error 将直接 重连
     */
    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
        super.onFailure(webSocket, t, response)
        log("onFailure")
        t.printStackTrace()
        changeStatus(ConnectStatus.ONFAILURE)
        reConnect()
        mSocketIOCallback?.onError(t)
    }

    /**
     * 接受socket回调数据
     * 返回数据不能包含pong
     * pong 默认与服务器ping 对应的回执数据
     */
    override fun onMessage(webSocket: WebSocket, text: String) {
        super.onMessage(webSocket, text)
        if (text.contains("pong")) {
            log("<=${text}:${Tools.format(System.currentTimeMillis())}")
            return
        } else {
            log("onMessage:$text")
        }
        mSocketIOCallback?.onMessage(text)
        changeStatus(ConnectStatus.CONNECTING)
    }

    /**
     * 接受socket回调数据 建议使用 onMessage(webSocket: WebSocket, text: String)
     */
    override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
        super.onMessage(webSocket, bytes)
        changeStatus(ConnectStatus.CONNECTING)
    }

    private fun changeStatus(connecting: ConnectStatus) {
        this.status = connecting
        mSocketIOCallback?.onConnect(status)
    }

    /**
     * 发送数据  建议json：与后台预定好格式
     */
    public fun send(textJson: String) {
        if (null != webSocket) {
            webSocket?.send(textJson)
            log("send:${textJson}")
        }
    }

    /**
     * 关闭websocket功能
     */
    public fun close() {
        try {
            if (null != handler) {
                handler?.removeMessages(HANDLER_MESSAGE_RECONN)
                handler = null
            }
            //关闭websocket功能
            changeStatus(ConnectStatus.CLOSING)
            if (null != webSocket) {
                webSocket?.cancel()
                webSocket?.close(1000, null)
                webSocket = null
                log("close")
            }
            //关闭心跳检测
            closeHeartBeat()
        } catch (e: Exception) {

        } finally {
        }
    }

    private var isDebug = true
    private fun log(message: String) {
        if (!isDebug) true//如果是debug 模式关闭日志输出
        Log.e(TAG, message);
    }

    //*********************检测心跳***********************
    private var HEART_BEAT_RATE: Long = 60 * 1000;//每隔10秒进行一次对长连接的心跳检测
    private var isCheckHeart = true
    private var sendTime = 0L
    private var count = 0
    private val heartBeatRunnable = Runnable {
        try {
            while (!Thread.interrupted() && isCheckHeart) {
                //重连的时候不执行ping
                if (this.status != ConnectStatus.RECONNECTING && this.status != ConnectStatus.CLOSED) {
                    if (this.status == ConnectStatus.CONNECTING || this.status == ConnectStatus.OPEN) {
                        //大于心跳时间 发ping
                        if ((System.currentTimeMillis() - sendTime) >= HEART_BEAT_RATE) {
                            val sendResult: Boolean = webSocket!!.send("ping")
                            if (!sendResult) {//长连接断开
                                count++
                                log("ping第" + count + "次")
                                //ping 5次没成功 重连
                                if (count >= 5) {
                                    if (status != ConnectStatus.RECONNECTING) {
                                        webSocket?.cancel()//断开以前的连接
                                        reConnect()//重新连接
                                    }
                                    count = 0
                                }
                            } else {//连接正常
                                count = 0
                                log("=>ping:${Tools.format(System.currentTimeMillis())}")
                            }
                            sendTime = System.currentTimeMillis()
                        }
                    }
                }
                Thread.sleep(HEART_BEAT_RATE)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            changeStatus(ConnectStatus.CLOSED)
            mSocketIOCallback?.onError(e)
            Thread.interrupted()
        } finally {

        }
    }
    private var checkHeartBeatThread: Thread? = null

    /**
     * 开启检查心跳功能
     */
    private fun checkHeart(): WSManger {
        if (null == checkHeartBeatThread) {
            checkHeartBeatThread = Thread(heartBeatRunnable)
        }
        checkHeartBeatThread?.start()
        return this
    }

    /**
     * 关闭心跳检测
     */
    private fun closeHeartBeat() {
        //关闭检测心跳现场
        if (null != checkHeartBeatThread) {
            checkHeartBeatThread?.interrupt()
        }
        isCheckHeart = false
    }

    /**
     * 设置心跳时间
     * 默认：60s
     */
    fun setHeartTime(time: Long): WSManger {
        this.HEART_BEAT_RATE = time;
        return this
    }

    private var userAgent: String? = null

    /**
     * 设置userAgent
     * 默认okhttp字符串
     */
    fun setUserAgent(userAgent: String): WSManger {
        this.userAgent = userAgent
        log(userAgent)
        return this
    }
}